查看原文
其他

Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析-[Android取经之路]

IngresGe IngresGe 2021-11-05

阅读本文大约需要花费20分钟。

系列文章:

Android取经之路——启动篇

Android系统架构-[Android取经之路]

Android是怎么启动的-[Android取经之路]

Android系统启动之init进程(一)-「Android取经之路」

Android系统启动之init进程(二)-「Android取经之路」

Android 10.0系统启动之init进程(三)-「Android取经之路」

Android 10.0系统启动之init进程(四)-「Android取经之路」

Android 10.0系统启动之Zygote进程(一)-「Android取经之路」

Android 10.0系统启动之Zygote进程(二)-「Android取经之路」

Android 10.0系统启动之Zygote进程(三)-「Android取经之路」

Android 10.0系统启动之Zygote进程(四)-「Android取经之路」

Android 10.0系统启动之SystemServer进程(一)-「Android取经之路」

Android 10.0系统启动之SystemServer进程(二)-「Android取经之路

Android 10.0系统服务之AMS启动流程-「Android取经之路」

Android10.0系统启动之Launcher(桌面)启动流程-[Android取经之路]

Android10.0应用进程创建过程以及Zygote的fork流程-[Android取经之路]

Android 10.0 PackageManagerService(一)工作原理及启动流程-[Android取经之路]

Android 10.0 PackageManagerService(二)权限扫描-[Android取经之路]

Android 10.0 PackageManagerService(三)APK扫描-[Android取经之路]

Android 10.0 PackageManagerService(四)APK安装流程-[Android取经之路]

Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性-[Android取经之路]

Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化-[Android取经之路]


    上一节我们看了Android日志系统的架构分析以及logd、logcat的初始化操作,这一节我们来看看日志系统的读写操作

    日志系统系列文章:

Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性-[Android取经之路]

Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化-[Android取经之路]


7.3 写日志

    写日志的过程,主要是通过liblog,把日志写入到/dev/socket/logdw, 守护进程logd监控logdw的写入信息,一旦发现有日志写入后,会把日志存入到LogListener的LogBuffer中。

日志写入调用栈如下:



7.3.1  [logger_write.cpp] __android_log_buf_write()

说明:JAVA层通过jni最终调用到liblog的 __android_log_buf_write(),组装结构体iovec,调用write_to_log()进行日志写入

源码:

int __android_log_buf_write(int bufID, int prio, const char* tag, const char* msg) { if (!tag) tag = ""; ... //组装结构体,包括tag、msg struct iovec vec[3]; vec[0].iov_base = (unsigned char*)&prio; vec[0].iov_len = 1; vec[1].iov_base = (void*)tag; vec[1].iov_len = strlen(tag) + 1; vec[2].iov_base = (void*)msg; vec[2].iov_len = strlen(msg) + 1; //进一步调用,进行日志的写入 return write_to_log(static_cast<log_id_t>(bufID), vec, 3);}


7.3.2 [logger_write.cpp] write_to_log()

write_to_log为一个函数指针,默认指向__write_to_log_init()

static int (*write_to_log)(log_id_t, struct iovec* vec, size_t nr) = __write_to_log_init;


7.3.3 [logger_write.cpp] __write_to_log_init()

说明:write_to_log 开始指向__write_to_log_init(),首先调用__write_to_log_initialize()进行日志配置初始化,再把write_to_log 指向__write_to_log_daemon(),调用__write_to_log_daemon()进行日志写入。

源码:

static int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) { int ret, save_errno = errno;
__android_log_lock();
//第一次调用时,write_to_log 指向__write_to_log_init(),此条件满足 if (write_to_log == __write_to_log_init) { //日志配置初始化,最终调用到logdLoggerWrite来进行日志的配置 ret = __write_to_log_initialize(); if (ret < 0) { __android_log_unlock(); //解锁log写入进程,避免死锁 if (!list_empty(&__android_log_persist_write)) { __write_to_log_daemon(log_id, vec, nr); } errno = save_errno; return ret; }
//write_to_log 指向__write_to_log_daemon write_to_log = __write_to_log_daemon; }
__android_log_unlock(); //通过__write_to_log_daemon进行日志写入 ret = write_to_log(log_id, vec, nr); errno = save_errno; return ret;}


7.3.4 日志配置

[logger_write.cpp]  __write_to_log_initialize()

说明:首先进行日志的配置,得到logdLoggerWrite 和pmsgLoggerWrite两个日志节点,包含了open、close、write的函数指针

源码:

static int __write_to_log_initialize() { struct android_log_transport_write* transport; struct listnode* n; int i = 0, ret = 0; //日志的配置 __android_log_config_write(); write_transport_for_each_safe(transport, n, &__android_log_transport_write) { //检查节点是否可用 __android_log_cache_available(transport); ... ++ret; } write_transport_for_each_safe(transport, n, &__android_log_persist_write) { //检查节点是否可用 __android_log_cache_available(transport); ... ++i; } if (!ret && !i) { return -ENODEV; }
return ret;}


[config_write.cpp] __android_log_config_write()

说明:对 "struct android_log_transport_write logdLoggerWrite" 和 "struct android_log_transport_write pmsgLoggerWrite" 的包装.

他们分别在 "/system/core/liblog/logd_writer.c" 和 "/system/core/liblog/pmsg_writer.c" 中实现

源码:

void __android_log_config_write() { if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {#if (FAKE_LOG_DEVICE == 0) extern struct android_log_transport_write logdLoggerWrite; extern struct android_log_transport_write pmsgLoggerWrite;
__android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite); __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);#else extern struct android_log_transport_write fakeLoggerWrite;
__android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);#endif }
if (__android_log_transport & LOGGER_STDERR) { extern struct android_log_transport_write stderrLoggerWrite;
if (list_empty(&__android_log_transport_write)) { __android_log_add_transport(&__android_log_transport_write, &stderrLoggerWrite); } else { struct android_log_transport_write* transp; write_transport_for_each(transp, &__android_log_transport_write) { if (transp == &stderrLoggerWrite) { return; } } __android_log_add_transport(&__android_log_persist_write, &stderrLoggerWrite); } }}


[logd_writer.c] logdLoggerWrite

说明:配置logdLoggerWrite的节点,有open、close、write操作,

logdOpen()中连接"/dev/socket/logdw",再通过logdWrite()把日志写入到logdw中

struct android_log_transport_write logdLoggerWrite = { .node = {&logdLoggerWrite.node, &logdLoggerWrite.node}, .context.sock = -EBADF, .name = "logd", .available = logdAvailable, .open = logdOpen, .close = logdClose, .write = logdWrite,};


[logd_writer.c] logdOpen()

说明:连接"/dev/socket/logdw" socket,记录socket句柄到 logdLoggerWrite.context.sock

static int logdOpen() { int i, ret = 0;
i = atomic_load(&logdLoggerWrite.context.sock); if (i < 0) { int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)); if (sock < 0) { ret = -errno; } else { struct sockaddr_un un; memset(&un, 0, sizeof(struct sockaddr_un)); un.sun_family = AF_UNIX; strcpy(un.sun_path, "/dev/socket/logdw");
if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) { ret = -errno; switch (ret) { case -ENOTCONN: case -ECONNREFUSED: case -ENOENT: i = atomic_exchange(&logdLoggerWrite.context.sock, ret); [[fallthrough]]; default: break; } close(sock); } else { ret = atomic_exchange(&logdLoggerWrite.context.sock, sock); if ((ret >= 0) && (ret != sock)) { close(ret); } ret = 0; } } }
return ret;}


[logd_writer.c] logdWrite()

说明:logdWrite() 通过调用writev,把log非阻塞地写入到socket .

static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) { ssize_t ret; int sock; static const unsigned headerLength = 1; struct iovec newVec[nr + headerLength]; android_log_header_t header; size_t i, payloadSize; static atomic_int dropped; static atomic_int droppedSecurity;
//原子操作,load logdLoggerWrite.context.sock sock = atomic_load(&logdLoggerWrite.context.sock); if (sock < 0) switch (sock) { case -ENOTCONN: case -ECONNREFUSED: case -ENOENT: break; default: return -EBADF; }
/* logd, after initialization and priv drop */ if (__android_log_uid() == AID_LOGD) { return 0; }

header.tid = gettid(); header.realtime.tv_sec = ts->tv_sec; header.realtime.tv_nsec = ts->tv_nsec;
newVec[0].iov_base = (unsigned char*)&header; newVec[0].iov_len = sizeof(header);
if (sock >= 0) { int32_t snapshot = atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed); if (snapshot) { android_log_event_int_t buffer;
header.id = LOG_ID_SECURITY; buffer.header.tag = htole32(LIBLOG_LOG_TAG); buffer.payload.type = EVENT_TYPE_INT; buffer.payload.data = htole32(snapshot);
newVec[headerLength].iov_base = &buffer; newVec[headerLength].iov_len = sizeof(buffer);
//调用writev,把日志写入logdw ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2)); if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) { atomic_fetch_add_explicit(&droppedSecurity, snapshot, memory_order_relaxed); } } snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed); if (snapshot && __android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog", strlen("liblog"), ANDROID_LOG_VERBOSE)) { android_log_event_int_t buffer;
header.id = LOG_ID_EVENTS; buffer.header.tag = htole32(LIBLOG_LOG_TAG); buffer.payload.type = EVENT_TYPE_INT; buffer.payload.data = htole32(snapshot);
newVec[headerLength].iov_base = &buffer; newVec[headerLength].iov_len = sizeof(buffer);
ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2)); if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) { atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed); } } }
header.id = logId;
for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) { newVec[i].iov_base = vec[i - headerLength].iov_base; payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) { newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD; if (newVec[i].iov_len) { ++i; } break; } }
/* * The write below could be lost, but will never block. * * ENOTCONN occurs if logd has died. * ENOENT occurs if logd is not running and socket is missing. * ECONNREFUSED occurs if we can not reconnect to logd. * EAGAIN occurs if logd is overloaded. */ if (sock < 0) { ret = sock; } else { ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i)); if (ret < 0) { ret = -errno; } } switch (ret) { case -ENOTCONN: case -ECONNREFUSED: case -ENOENT: if (__android_log_trylock()) { return ret; /* in a signal handler? try again when less stressed */ } __logdClose(ret); ret = logdOpen(); __android_log_unlock();
if (ret < 0) { return ret; }
ret = TEMP_FAILURE_RETRY(writev(atomic_load(&logdLoggerWrite.context.sock), newVec, i)); if (ret < 0) { ret = -errno; } [[fallthrough]]; default: break; }
if (ret > (ssize_t)sizeof(header)) { ret -= sizeof(header); } else if (ret == -EAGAIN) { atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed); if (logId == LOG_ID_SECURITY) { atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed); } }
return ret;}


[pmsg_writer.c] pmsgLoggerWrite

说明:配置pmsgLoggerWrite的节点,pmsgOpen()打开"/dev/pmsg0",在通过pmsgWrite()把日志写入到"/dev/pmsg0"中

struct android_log_transport_write pmsgLoggerWrite = { .node = {&pmsgLoggerWrite.node, &pmsgLoggerWrite.node}, .context.fd = -1, .name = "pmsg", .available = pmsgAvailable, .open = pmsgOpen, .close = pmsgClose, .write = pmsgWrite,};


[pmsg_writer.c] pmsgOpen()

说明:打开"/dev/pmsg0"

static int pmsgOpen() { int fd = atomic_load(&pmsgLoggerWrite.context.fd); if (fd < 0) { int i;
fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY | O_CLOEXEC)); i = atomic_exchange(&pmsgLoggerWrite.context.fd, fd); if ((i >= 0) && (i != fd)) { close(i); } }
return fd;}


[pmsg_writer.c] pmsgWrite()

说明:日志写入到"/dev/pmsg0"

static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) { static const unsigned headerLength = 2; struct iovec newVec[nr + headerLength]; android_log_header_t header; android_pmsg_log_header_t pmsgHeader; size_t i, payloadSize; ssize_t ret;
if ((logId == LOG_ID_EVENTS) && !__android_log_is_debuggable()) { if (vec[0].iov_len < 4) { return -EINVAL; }
if (SNET_EVENT_LOG_TAG != get4LE(static_cast<uint8_t*>(vec[0].iov_base))) { return -EPERM; } }
if (atomic_load(&pmsgLoggerWrite.context.fd) < 0) { return -EBADF; }
pmsgHeader.magic = LOGGER_MAGIC; pmsgHeader.len = sizeof(pmsgHeader) + sizeof(header); pmsgHeader.uid = __android_log_uid(); pmsgHeader.pid = getpid();
header.id = logId; header.tid = gettid(); header.realtime.tv_sec = ts->tv_sec; header.realtime.tv_nsec = ts->tv_nsec;
newVec[0].iov_base = (unsigned char*)&pmsgHeader; newVec[0].iov_len = sizeof(pmsgHeader); newVec[1].iov_base = (unsigned char*)&header; newVec[1].iov_len = sizeof(header);
for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) { newVec[i].iov_base = vec[i - headerLength].iov_base; payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) { newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD; if (newVec[i].iov_len) { ++i; } payloadSize = LOGGER_ENTRY_MAX_PAYLOAD; break; } } pmsgHeader.len += payloadSize;
ret = TEMP_FAILURE_RETRY(writev(atomic_load(&pmsgLoggerWrite.context.fd), newVec, i)); if (ret < 0) { ret = errno ? -errno : -ENOTCONN; }
if (ret > (ssize_t)(sizeof(header) + sizeof(pmsgHeader))) { ret -= sizeof(header) - sizeof(pmsgHeader); }
return ret;}


7.3.4 日志写入 /dev/socket/logdw

    在前面,日志配置已经完成,那么接下来调用__write_to_log_daemon()进行日志的真正写入

[logger_write.cpp] __write_to_log_daemon()

说明:最终通过获取logdLoggerWrite 和 pmsgLoggerWrite的write操作,即 调用logdWrite()\pmsgWrite()进行日志的写入

源码:

static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) { struct android_log_transport_write* node; int ret, save_errno; struct timespec ts; size_t len, i;
for (len = i = 0; i < nr; ++i) { len += vec[i].iov_len; } if (!len) { return -EINVAL; }
save_errno = errno;#if defined(__ANDROID__) clock_gettime(android_log_clockid(), &ts);
if (log_id == LOG_ID_SECURITY) { if (vec[0].iov_len < 4) { errno = save_errno; return -EINVAL; }
ret = check_log_uid_permissions(); if (ret < 0) { errno = save_errno; return ret; } if (!__android_log_security()) { /* If only we could reset downstream logd counter */ errno = save_errno; return -EPERM; } } else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) { const char* tag; size_t len; EventTagMap *m, *f;
if (vec[0].iov_len < 4) { errno = save_errno; return -EINVAL; }
tag = NULL; len = 0; f = NULL; m = (EventTagMap*)atomic_load(&tagMap);
if (!m) { ret = __android_log_trylock(); m = (EventTagMap*)atomic_load(&tagMap); /* trylock flush cache */ if (!m) { m = android_openEventTagMap(NULL); if (ret) { /* trylock failed, use local copy, mark for close */ f = m; } else { if (!m) { /* One chance to open map file */ m = (EventTagMap*)(uintptr_t)-1LL; } atomic_store(&tagMap, (uintptr_t)m); } } if (!ret) { /* trylock succeeded, unlock */ __android_log_unlock(); } } if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) { tag = android_lookupEventTag_len(m, &len, get4LE(static_cast<uint8_t*>(vec[0].iov_base))); } ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len, ANDROID_LOG_VERBOSE); if (f) { /* local copy marked for close */ android_closeEventTagMap(f); } if (!ret) { errno = save_errno; return -EPERM; } } else { /* Validate the incoming tag, tag content can not split across iovec */ char prio = ANDROID_LOG_VERBOSE; const char* tag = static_cast<const char*>(vec[0].iov_base); size_t len = vec[0].iov_len; if (!tag) { len = 0; } if (len > 0) { prio = *tag; if (len > 1) { --len; ++tag; } else { len = vec[1].iov_len; tag = ((const char*)vec[1].iov_base); if (!tag) { len = 0; } } } /* tag must be nul terminated */ if (tag && strnlen(tag, len) >= len) { tag = NULL; }
if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) { errno = save_errno; return -EPERM; } }#else /* simulate clock_gettime(CLOCK_REALTIME, &ts); */ { struct timeval tv; gettimeofday(&tv, NULL); ts.tv_sec = tv.tv_sec; ts.tv_nsec = tv.tv_usec * 1000; }#endif
ret = 0; i = 1 << log_id; write_transport_for_each(node, &__android_log_transport_write) { if (node->logMask & i) { ssize_t retval; //从logdLoggerWrite中拿到write操作,即logdWrite()进行日志的写入 retval = (*node->write)(log_id, &ts, vec, nr); if (ret >= 0) { ret = retval; } } }
write_transport_for_each(node, &__android_log_persist_write) { if (node->logMask & i) { //从pmsgLoggerWrite中拿到write操作,即pmsgWrite()进行日志的写入 (void)(*node->write)(log_id, &ts, vec, nr); } }
errno = save_errno; return ret;}


7.3.5 日志写入LogListener的LogBuffer

在前面logd初始化时,我们能够看到LogListener中会先创建一个LogBuffer,LogBuffer在初始化init()中会初始化各个log的域的大小(如main、crash、system等默认为256k),接着会启动一个SocketListener,对/dev/socket/logdw进行监听,

  [/system/core/logd/main.cpp]

int main(int argc, char* argv[]) { ... LogListener* swl = new LogListener(logBuf, reader); if (swl->startListener(600)) { return EXIT_FAILURE; }... }


LogListene的对象创建时,会先调用 getLogSocket(),对logdw进行监听。

int LogListener::getLogSocket() { static const char socketName[] = "logdw"; int sock = android_get_control_socket(socketName);
if (sock < 0) { // logd started up in init.sh sock = socket_local_server( socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM);
int on = 1; if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { return -1; } } return sock;}


在startListener函数中创建线程,线程注册函数为SocketListener::threadStart;

执行runListener函数,如果socket监听到数据,则执行onDataAvailable函数进行处理;

调用logbuf->log(LogBuffer::log),这个函数很重要,新建一个LogBufferElement对象(用于保存log),调用mLogElements.insert将LogBufferElement加入list容器,实现log的保存。

源码:

bool LogListener::onDataAvailable(SocketClient* cli) { static bool name_set; if (!name_set) { prctl(PR_SET_NAME, "logd.writer"); name_set = true; }
// + 1 to ensure null terminator if MAX_PAYLOAD buffer is received char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time) + LOGGER_ENTRY_MAX_PAYLOAD + 1]; struct iovec iov = { buffer, sizeof(buffer) - 1 };
//将日志写入list容器 int res = logbuf->log(logId, header->realtime, cred->uid, cred->pid, header->tid, msg, ((size_t)n <= UINT16_MAX) ? (uint16_t)n : UINT16_MAX); if (res > 0) { reader->notifyNewLog(static_cast<log_mask_t>(1 << logId)); }
return true;}


7.4 读取日志

    读取日志时,主要通过logcat工具来抓取。

    Logcat是通过liblog 连接 "/dev/socket/logdr" 来获取日志。logcat通过调用liblog 的函数android_logger_list_read()进行日志的读取,最终是连接"/dev/socket/logdr",并读取其中的日志。

    logdr的日志由 logd的LogReader 进行写入。

    日志读取调用栈如下:


    logcat启动后,会调用android_logcat_run_command(),最终进入__logcat()来解析command,并读取日志,我们就从__logcat()来进行分析


7.4.1 [logcat.cpp] __logcat()

说明:__logcat()首先解析传入的command,然后进行日志的读取,如果出现日志读取失败,则退出logcat进程,否则手动启动的logcat进行一直处于运行状态

源码:

static int __logcat(android_logcat_context_internal* context) { ... struct logger_list* logger_list; ... //logcat command的解析 while (true) { int c = getopt_long(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options, &option_index); switch (c) { ... case 'g': if (!optarg) { getLogSize = true; break; } FALLTHROUGH_INTENDED; case 'b': { std::unique_ptr<char, void (*)(void*)> buffers(strdup(optarg), free); char* arg = buffers.get(); unsigned idMask = 0; char* sv = nullptr; // protect against -ENOMEM above while (!!(arg = strtok_r(arg, delimiters, &sv))) { if (!strcmp(arg, "default")) { idMask |= (1 << LOG_ID_MAIN) | (1 << LOG_ID_SYSTEM) | (1 << LOG_ID_CRASH); } else if (!strcmp(arg, "all")) { allSelected = true; idMask = (unsigned)-1; } else { log_id_t log_id = android_name_to_log_id(arg); const char* name = android_log_id_to_name(log_id);
if (!!strcmp(name, arg)) { logcat_panic(context, HELP_TRUE, "unknown buffer %s\n", arg); goto exit; } if (log_id == LOG_ID_SECURITY) allSelected = false; idMask |= (1 << log_id); } arg = nullptr; }
for (int i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) { const char* name = android_log_id_to_name((log_id_t)i); log_id_t log_id = android_name_to_log_id(name);
if (log_id != (log_id_t)i) continue; if (!(idMask & (1 << i))) continue;
bool found = false; for (dev = context->devices; dev; dev = dev->next) { if (!strcmp(name, dev->device)) { found = true; break; } if (!dev->next) break; } if (found) continue;
bool binary = !strcmp(name, "events") || !strcmp(name, "security") || !strcmp(name, "stats"); log_device_t* d = new log_device_t(name, binary);
if (dev) { dev->next = d; dev = d; } else { context->devices = dev = d; } context->devCount++; } } ... case 'h': show_help(context); show_format_help(context); goto exit;
default: logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n", optopt); goto exit; } } ... while (!context->stop && (!context->maxCount || (context->printCount < context->maxCount))) { struct log_msg log_msg; //调用liblog的接口,读取"/dev/socket/logdr"来读取日志 int ret = android_logger_list_read(logger_list, &log_msg); if (!ret) { logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n"); break; } ... }close: // Short and sweet. Implemented generic version in android_logcat_destroy. while (!!(dev = context->devices)) { context->devices = dev->next; delete dev; } android_logger_list_free(logger_list);
exit: // close write end of pipe to help things along if (context->output_fd == context->fds[1]) { android::close_output(context); } if (context->error_fd == context->fds[1]) { android::close_error(context); } if (context->fds[1] >= 0) { // NB: should be closed by the above int save_errno = errno; close(context->fds[1]); errno = save_errno; context->fds[1] = -1; } context->thread_stopped = true; return context->retval;}



下面进入liblog的android_logger_list_read()来进一步分析:


7.4.2 [logger_read.cpp] android_logger_list_read()

说明:先初始化logger_list,获得logdLoggerRead的节点,在进行transport_context的赋值操作,最终调用logdLoggerRead中的logdRead()进行日志读取

源码:

int android_logger_list_read(struct logger_list* logger_list, struct log_msg* log_msg) { struct android_log_logger_list* logger_list_internal = (struct android_log_logger_list*)logger_list;
//1. 初始化logger_list,把logdLoggerRead 和 pmsgLoggerRead 两个节点按需传给transoprt,我们一般使用 logdLoggerRead int ret = init_transport_context(logger_list_internal); if (ret < 0) { return ret; }
//2. transoport的赋值 android_log_transport_context* transport_context = &logger_list_internal->transport_context; //3.通过调用 logger_list中read方法,即logdLoggerRead中的logdRead()进行日志读取 return android_transport_read(logger_list_internal, transport_context, log_msg);}


7.4.3 [logger_read.cpp] init_transport_context()

说明:根据logger_list->mode来决定使用哪种日志读取节点,我们大部分情况下使用logdLoggerRead,并进行赋值,主要内容就是得到 logdLoggerRead的节点,为后续日志读取提供服务

源码:

static int init_transport_context(struct android_log_logger_list* logger_list) { if (!logger_list) { return -EINVAL; }
if (list_empty(&logger_list->logger)) { return -EINVAL; }
if (logger_list->transport_initialized) { return 0; }
#if (FAKE_LOG_DEVICE == 0) extern struct android_log_transport_read logdLoggerRead; extern struct android_log_transport_read pmsgLoggerRead;
struct android_log_transport_read* transport; //根据logger_list->mode来决定使用哪种日志读取节点,我们大部分情况下使用logdLoggerRead transport = (logger_list->mode & ANDROID_LOG_PSTORE) ? &pmsgLoggerRead : &logdLoggerRead;
struct android_log_logger* logger; unsigned logMask = 0;
//logger的校验 logger_for_each(logger, logger_list) { log_id_t logId = logger->logId;
if (logId == LOG_ID_SECURITY && __android_log_uid() != AID_SYSTEM) { continue; } if (transport->read && (!transport->available || transport->available(logId) >= 0)) { logMask |= 1 << logId; } } if (!logMask) { return -ENODEV; }
//节点赋值 logger_list->transport_context.transport = transport; logger_list->transport_context.logMask = logMask; logger_list->transport_context.ret = 1;#endif return 0;}


logdLoggerRead  中提供了 read、poll、close等操作:

struct android_log_transport_read logdLoggerRead = { .node = {&logdLoggerRead.node, &logdLoggerRead.node}, .name = "logd", .available = logdAvailable, .version = logdVersion, .read = logdRead, .poll = logdPoll, .close = logdClose, .clear = logdClear, .getSize = logdGetSize, .setSize = logdSetSize, .getReadableSize = logdGetReadableSize, .getPrune = logdGetPrune, .setPrune = logdSetPrune, .getStats = logdGetStats,};


7.4.4 [logger_read.cpp] android_transport_read()

说明: 调用的logdRead()来读取日志,并组装日志 msg,供logcat进行展示

源码:

static int android_transport_read(struct android_log_logger_list* logger_list, struct android_log_transport_context* transp, struct log_msg* log_msg) { // 根据7.4.3 克制最终调用的是logdRead()来读取日志 int ret = (*transp->transport->read)(logger_list, transp, log_msg);
if (ret > (int)sizeof(*log_msg)) { ret = sizeof(*log_msg); }
transp->ret = ret;
/* propagate errors, or make sure len & hdr_size members visible */ if (ret < (int)(sizeof(log_msg->entry.len) + sizeof(log_msg->entry.hdr_size))) { if (ret >= (int)sizeof(log_msg->entry.len)) { log_msg->entry.len = 0; } return ret; }
/* hdr_size correction (logger_entry -> logger_entry_v2+ conversion) */ if (log_msg->entry_v2.hdr_size == 0) { log_msg->entry_v2.hdr_size = sizeof(struct logger_entry); } if ((log_msg->entry_v2.hdr_size < sizeof(log_msg->entry_v1)) || (log_msg->entry_v2.hdr_size > sizeof(log_msg->entry))) { return -EINVAL; }
/* len validation */ if (ret <= log_msg->entry_v2.hdr_size) { log_msg->entry.len = 0; } else { log_msg->entry.len = ret - log_msg->entry_v2.hdr_size; }
return ret;}


7.4.5 [logd_reader.cpp] logdRead()

说明:连接socket "/dev/socket/lodgr",读取logdr 的日志

源码:

static int logdRead(struct android_log_logger_list* logger_list, struct android_log_transport_context* transp, struct log_msg* log_msg) { int ret, e; struct sigaction ignore; struct sigaction old_sigaction; unsigned int old_alarm = 0; //连接socket "/dev/socket/lodgr" ret = logdOpen(logger_list, transp); if (ret < 0) { return ret; }
memset(log_msg, 0, sizeof(*log_msg));
unsigned int new_alarm = 0; if (logger_list->mode & ANDROID_LOG_NONBLOCK) { if ((logger_list->mode & ANDROID_LOG_WRAP) && (logger_list->start.tv_sec || logger_list->start.tv_nsec)) { /* b/64143705 */ new_alarm = (ANDROID_LOG_WRAP_DEFAULT_TIMEOUT * 11) / 10 + 10; logger_list->mode &= ~ANDROID_LOG_WRAP; } else { new_alarm = 30; }
memset(&ignore, 0, sizeof(ignore)); ignore.sa_handler = caught_signal; sigemptyset(&ignore.sa_mask); /* particularily useful if tombstone is reporting for logd */ sigaction(SIGALRM, &ignore, &old_sigaction); old_alarm = alarm(new_alarm); }
//读取logdr 的日志 ret = recv(ret, log_msg, LOGGER_ENTRY_MAX_LEN, 0); e = errno;
if (new_alarm) { if ((ret == 0) || (e == EINTR)) { e = EAGAIN; ret = -1; } alarm(old_alarm); sigaction(SIGALRM, &old_sigaction, NULL); }
if ((ret == -1) && e) { return -e; } return ret;}


    至此,我们知道了logcat读取日志的流程,但是logdr的日志是由谁写入的呢,让我们接下来继续分析。在前面logd的初始化时,我们注意到创建了一个LogReader对象,其中存储了一个LogBuffer.

    

7.4.6 logd  LogReader

LogReader被创建后,会调用getLogSocket()来连接。"/dev/socket/logdr"这个socket。

int LogReader::getLogSocket() { static const char socketName[] = "logdr"; int sock = android_get_control_socket(socketName);
if (sock < 0) { sock = socket_local_server( socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET); }
return sock;}


    在 [7.3.5] 中,当有日志写入logdw时,LogListener()的onDataAvailable生效,先把日志写入LogBufer后,再LogReader的notifyNewLog()来通知有新的日志写入。

    当收到一个新的日志条目可用时,通知正在监视此条目的日志id的侦听socket,即我们可以进行读日志了。

void LogReader::notifyNewLog(log_mask_t logMask) { // 创建一个FlushCommand 对象,传入LogReader的对象和logmask FlushCommand command(*this, logMask); //调用socket接口,最终进入 logd的runSocketCommand() runOnEachSocket(&command);}
运行FlushCommand 的runSocketCommand()void SocketListener::runOnEachSocket(SocketClientCommand *command) { SocketClientCollection safeList; ... while (!safeList.empty()) { ... command->runSocketCommand(c); ... }}


[FlushCommand.cpp] runSocketCommand()

说明:对日志读取器套接字上的每个打开的客户端调用一次runSocketCommand。主要有三个command:LogListener、LogAudit、LogKlog。

对日志reader socket上的每个打开一次客户端就调用一次runSocketCommand。

在这里,我们管理并关联reader-client的跟踪和日志区域,锁定logtimeentry的LastLogTimes列表,并产生一个临时的客户端线程来讲数据归档到socket。

全局LogTimeEntry::wrlock()用于保护访问,引用计数用于确保在不受保护时管理单个LogTimeEntry生存期。

源码:

void FlushCommand::runSocketCommand(SocketClient* client) { LogTimeEntry* entry = nullptr; LastLogTimes& times = mReader.logbuf().mTimes;
LogTimeEntry::wrlock(); LastLogTimes::iterator it = times.begin(); while (it != times.end()) { entry = it->get(); if (entry->mClient == client) { if (!entry->isWatchingMultiple(mLogMask)) { LogTimeEntry::unlock(); return; } if (entry->mTimeout.tv_sec || entry->mTimeout.tv_nsec) { if (mReader.logbuf().isMonotonic()) { LogTimeEntry::unlock(); return; } // If the user changes the time in a gross manner that // invalidates the timeout, fall through and trigger. log_time now(CLOCK_REALTIME); if (((entry->mEnd + entry->mTimeout) > now) && (now > entry->mEnd)) { LogTimeEntry::unlock(); return; } } entry->triggerReader_Locked(); LogTimeEntry::unlock(); return; } it++; }
LogTimeEntry::unlock();}


如果socket监听到数据,则执行onDataAvailable(),进入LogReader的onDataAvailable()

[LogReader.cpp] onDataAvailable()

说明:相应日志读操作,先读入logdr中传入的客户端参数,然后把之前LogBuffer中的日志通过flushto,最终通过socket的sendDatav()写给client,比如logcat,所以我们可以开启多个logcat来获取日志。

源码:

bool LogReader::onDataAvailable(SocketClient* cli) { static bool name_set; //响应日志读操作 if (!name_set) { prctl(PR_SET_NAME, "logd.reader"); name_set = true; }
char buffer[255]; //读取客户端传入参数 int len = read(cli->getSocket(), buffer, sizeof(buffer) - 1); if (len <= 0) { doSocketDelete(cli); return false; } buffer[len] = '\0';
// Clients are only allowed to send one command, disconnect them if they // send another. //客户端只允许发送一个命令,如果它们发送另一个命令,则断开它们的连接 LogTimeEntry::wrlock(); for (const auto& entry : mLogbuf.mTimes) { if (entry->mClient == cli) { entry->release_Locked(); LogTimeEntry::unlock(); return false; } } LogTimeEntry::unlock(); ... log_time sequence = start; ... if (nonBlock && (sequence != log_time::EPOCH) && timeout) { .. //LogBuffer中的日志写入传入的client,即写入logcat logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli), FlushCommand::hasSecurityLogs(cli), logFindStart.callback, &logFindStart);
if (!logFindStart.found()) { doSocketDelete(cli); return false; } } ... LogTimeEntry::wrlock(); ... setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char*)&t, sizeof(t));
LogTimeEntry::unlock();
return true;}


至此日志的读取操作完成。下一节我们一起来看看<logd中如何进行selinux、kernel日志的读写>


: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存